#include <iostream>
#include <fstream>
#include <string>
#include <Windows.h>
#include <opencv/cv.h>
#include <opencv/cxcore.h>
#include <opencv/highgui.h>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>

#define Distort

using namespace std;

string strTimeSave;
bool isStopped = false;

string getTimeinString()
{
    time_t timeObj;
    time(&timeObj);
    tm *pTime = gmtime(&timeObj);
    char buffer[100];
	sprintf(buffer, "%02d%02d%02d_%02d%02d%02d",    pTime->tm_year+1900,pTime->tm_mon+1,pTime->tm_mday,
		pTime->tm_hour+7, pTime->tm_min, pTime->tm_sec);
    return buffer;
}

DWORD WINAPI Thread_no_1( LPVOID lpParam ) 
{
 
    int     Data = 0;
    int     count = 0;
    HANDLE  hStdout = NULL;
    
    // Get Handle To screen.
    // Else how will we print?
    if( (hStdout = 
         GetStdHandle(STD_OUTPUT_HANDLE)) 
         == INVALID_HANDLE_VALUE )  
    return 1;
	
	//Funtion Content
	std::string fileInput = "http://root:pass@172.16.123.1/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=30&.mjpg";

	string fileIntrinsics = "Test123_2_69_Intrinsics.xml";
	string fileDistortion = "Test123_2_69_Distortion.xml";

	CvCapture *capture=cvCaptureFromFile(fileInput.c_str());
	if(!capture) 
	{ 
		printf("\nCouldn't open the camera\n"); 
		return -1;
	}
	IplImage *image = cvQueryFrame( capture );
	
	string videoSourceFilePath = "SaveVideos/123_" + strTimeSave + ".avi";
		
	cv::VideoWriter oVideoWriter(videoSourceFilePath, CV_FOURCC('M', 'P', '4', '2'), 20, cv::Size(640, 480), true); //initialize the VideoWriter object 
	if ( !oVideoWriter.isOpened() ) //if not initialize the VideoWriter successfully, exit the program
	{
		std::cout << "ERROR: Failed to write the video" << std::endl;
		return -2;
	}

	unsigned int nFrame = 0;
	
#ifdef Distort
	CvMat *intrinsic = (CvMat*)cvLoad(fileIntrinsics.c_str());
	CvMat *distortion = (CvMat*)cvLoad(fileDistortion.c_str());

	// Build the undistort map used for all subsequent frames.
	IplImage* mapx = cvCreateImage( cvGetSize(image), IPL_DEPTH_32F, 1 );
	IplImage* mapy = cvCreateImage( cvGetSize(image), IPL_DEPTH_32F, 1 );

	cvInitUndistortMap(intrinsic,distortion,mapx,mapy);
#endif
	// Run the camera to the screen, showing the raw and the undistorted image.
	cvNamedWindow( "CAM 1 Undistort" );
	while(!isStopped && image) 
	{
		std::cout << "CAM 1. Frame Id = " << nFrame++ << std::endl;

		IplImage *t = cvCloneImage(image);
		cvShowImage( "CAM 1  Raw Video", image );			// Show raw image

#ifdef Distort
		cvRemap( t, image, mapx, mapy );			// Undistort image
		cvReleaseImage(&t);
		cvShowImage("CAM 1 Undistort", image);			// Show corrected image
#endif
		//Save to file
		cv::Mat tmp = cv::Mat(image);
		oVideoWriter.write(tmp);

		//Handle pause/unpause and ESC
		int c = cvWaitKey(15);
		if(c == 'p')
		{ 
			c = 0;
			//cvSaveImage("out.jpg", image);
			while(c != 'p' && c != 27)
			{
				c = cvWaitKey(250);
			}
		}
		if(c == 27)
			isStopped = true;

		image = cvQueryFrame( capture );
	} 

	oVideoWriter.release();
		
	return 1;
}

DWORD WINAPI Thread_no_2( LPVOID lpParam ) 
{
 
    int     Data = 0;
    int     count = 0;
    HANDLE  hStdout = NULL;
    
    // Get Handle To screen.
    // Else how will we print?
    if( (hStdout = 
         GetStdHandle(STD_OUTPUT_HANDLE)) 
         == INVALID_HANDLE_VALUE )  
    return 1;
	
	//Funtion Content
	std::string fileInput = "http://root:pass@172.16.100.205/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=30&.mjpg";

	string fileIntrinsics = "Test205_1_68_Intrinsics.xml";
	string fileDistortion = "Test205_1_68_Distortion.xml";

	CvCapture *capture=cvCaptureFromFile(fileInput.c_str());
	if(!capture) 
	{ 
		printf("\nCouldn't open the camera\n"); 
		return -1;
	}
	IplImage *image = cvQueryFrame( capture );
	
	string videoSourceFilePath = "SaveVideos/205_" + strTimeSave + ".avi";
		
	cv::VideoWriter oVideoWriter(videoSourceFilePath, CV_FOURCC('M', 'P', '4', '2'), 20, cv::Size(640, 480), true); //initialize the VideoWriter object 
	if ( !oVideoWriter.isOpened() ) //if not initialize the VideoWriter successfully, exit the program
	{
		std::cout << "ERROR: Failed to write the video" << std::endl;
		return -2;
	}

	unsigned int nFrame = 0;
	
#ifdef Distort
	CvMat *intrinsic = (CvMat*)cvLoad(fileIntrinsics.c_str());
	CvMat *distortion = (CvMat*)cvLoad(fileDistortion.c_str());

	// Build the undistort map used for all subsequent frames.
	IplImage* mapx = cvCreateImage( cvGetSize(image), IPL_DEPTH_32F, 1 );
	IplImage* mapy = cvCreateImage( cvGetSize(image), IPL_DEPTH_32F, 1 );

	cvInitUndistortMap(intrinsic,distortion,mapx,mapy);
#endif
	// Run the camera to the screen, showing the raw and the undistorted image.
	cvNamedWindow( "CAM 2 Undistort" );
	while(!isStopped && image) 
	{
		std::cout << "CAM 2. Frame Id = " << nFrame++ << std::endl;

		IplImage *t = cvCloneImage(image);
		cvShowImage( "CAM 2 Raw Video", image );			// Show raw image

#ifdef Distort
		cvRemap( t, image, mapx, mapy );			// Undistort image
		cvReleaseImage(&t);
		cvShowImage("CAM 2 Undistort", image);			// Show corrected image
#endif
		//Save to file
		cv::Mat tmp = cv::Mat(image);
		oVideoWriter.write(tmp);

		//Handle pause/unpause and ESC
		int c = cvWaitKey(15);
		if(c == 'p')
		{ 
			c = 0;
			//cvSaveImage("out.jpg", image);
			while(c != 'p' && c != 27)
			{
				c = cvWaitKey(250);
			}
		}
		if(c == 27)
			isStopped = true;

		image = cvQueryFrame( capture );
	} 

	oVideoWriter.release();
		
	return 1;
}

DWORD WINAPI Thread_no_3( LPVOID lpParam ) 
{
 
    int     Data = 0;
    int     count = 0;
    HANDLE  hStdout = NULL;
    
    // Get Handle To screen.
    // Else how will we print?
    if( (hStdout = 
         GetStdHandle(STD_OUTPUT_HANDLE)) 
         == INVALID_HANDLE_VALUE )  
    return 1;

	cv::Mat image;
	cv::VideoCapture vcap;
	string fileInput;
	string fileOutput;

	std::string AxisAddress = "http://root:pass@172.16.123.1/axis-cgi/mjpg/video.cgi?resolution=640x480&req_fps=30&.mjpg";

	if (!vcap.open(AxisAddress))
	{
		std::cout << "ERROR: Failed to load the video" << std::endl;
		return -1;
	}
	
	string videoSourceFilePath = "Source1_" + strTimeSave + ".avi";
		
	cv::VideoWriter oVideoWriter(videoSourceFilePath, CV_FOURCC('M', 'P', '4', '2'), 20, cv::Size(640, 480), true); //initialize the VideoWriter object 
	if ( !oVideoWriter.isOpened() ) //if not initialize the VideoWriter successfully, exit the program
	{
		std::cout << "ERROR: Failed to write the video" << std::endl;
		return -2;
	}

	unsigned int nFrame = 0;
	int stopc = 0;
	while(stopc != 27) 
	{
		if(!vcap.read(image)) 
		{	
			std::cout << "No frame on CAM 1" << std::endl;
			return 0;
		}

		std::cout << "CAM 1. Frame Id = " << nFrame++ << std::endl;

		oVideoWriter.write(image);
		
		cv::imshow("Source CAM 1",image);

		stopc = cv::waitKey(33);

	}

	oVideoWriter.release();
		
	return 1;
}

int main(int, char**)
{

	strTimeSave = getTimeinString();
  
    int Data_Of_Thread_1 = 1;
	// Data of Thread 2
    int Data_Of_Thread_2 = 2;
	
	// variable to hold handle of Thread 1
    HANDLE Handle_Of_Thread_1 = 0;
    // variable to hold handle of Thread 2 
    HANDLE Handle_Of_Thread_2 = 0;

	 // Aray to store thread handles 
    HANDLE Array_Of_Thread_Handles[2];

	 // Create thread 1.
    Handle_Of_Thread_1 = CreateThread( NULL, 0, Thread_no_1, &Data_Of_Thread_1, 0, NULL);  
    if ( Handle_Of_Thread_1 == NULL)
        ExitProcess(Data_Of_Thread_1);
    
    // Create thread 2.
    Handle_Of_Thread_2 = CreateThread( NULL, 0, Thread_no_2, &Data_Of_Thread_2, 0, NULL);  
    if ( Handle_Of_Thread_2 == NULL)
        ExitProcess(Data_Of_Thread_2);

	  // Store Thread handles in Array of Thread
    // Handles as per the requirement
    // of WaitForMultipleObjects() 
    Array_Of_Thread_Handles[0] = Handle_Of_Thread_1;
    Array_Of_Thread_Handles[1] = Handle_Of_Thread_2;

	WaitForMultipleObjects( 2, Array_Of_Thread_Handles, TRUE, INFINITE);
	
	// Close all thread handles upon completion.
	CloseHandle(Handle_Of_Thread_1);
	CloseHandle(Handle_Of_Thread_2);

	return 1;
}